export default {
  "show_ad": true,
  "is_author": false,
  "liked_note": false,
  "public_comment_count": 52,
  "featured_comments_count": 0,
  "likes_count": 256,
  "trial_open": false,
  "wangxin_trial_open": null,
  "description": "最近花了几天时间研究了一下怎么用iOS原生页面高效的更新app页面样式，我的思路是使用仿H5样式的JSON数据，利用各种布局（流动，线性等布局）来动态更新页面样式。 页面动态...",
  "gupao_eligible": 0,
  "total_fp_amount": 4757,
  "vip_note": false,
  "id": 11818622,
  "slug": "9e22a732c9c8",
  "public_title": "iOS页面动态化，怎么样用JSON数据的原生页面摆脱低效的H5页面，来动态更新app页面样式",
  "free_content": "<blockquote>\n<p>最近花了几天时间研究了一下怎么用iOS原生页面高效的更新app页面样式，我的思路是使用仿H5样式的JSON数据，利用各种布局（流动，线性等布局）来动态更新页面样式。</p>\n</blockquote>\n<p>页面动态化主要的使用场景是app的电商页面，由于电商的业务需求较大，经常会需要改变各个banner的样式来满足业务的需求，所以需要客户端根据数据实时更新样式，下面说说我对页面动态化的理解跟思路。先看一下最后demo的效果：</p>\n<div class=\"image-package\">\n<div class=\"image-container\" style=\"max-width: 394px; max-height: 1282px;\">\n<div class=\"image-container-fill\" style=\"padding-bottom: 183.25%;\"></div>\n<div class=\"image-view\" data-width=\"394\" data-height=\"722\"><img data-original-src=\"//upload-images.jianshu.io/upload_images/1932655-3a5620c71c75fe9c.gif\" data-original-width=\"394\" data-original-height=\"722\" data-original-format=\"image/gif\" data-original-filesize=\"945616\"></div>\n</div>\n<div class=\"image-caption\">2017-04-27 18_34_37.gif</div>\n</div>\n<h3>其他app是怎么做的</h3>\n<p>用Charles抓包，发现京东跟网易考拉都是用type这个字段提前定义好各种banner的样式，通过后台返回的数据，来实现页面动态化更新，这样做有好的方面也有不好的地方：</p>\n<ul>\n<li>好的方面：提前定义好banner的样式，客户端只需要定义好各个样式的UI，根据type字段来展现就好</li>\n<li>不好的方面：只能根据定义好的样式来进行动态化更新，如果需要加入新的样式，需要提交新包</li>\n</ul>\n<h3>我的做法</h3>\n<p>客户端不定义banner样式，而是定义布局样式来实现高度动态更新页面，思路来自于CSS里定义的div+CSS布局，CSS里可以根据display的inline跟block值来进行布局，iOS里，我定义了几种常见的布局：</p>\n<ul>\n<li>&lt;b&gt;FlowLayout&lt;/b&gt;：流式布局，根据display值横向或者纵向布局，根据屏幕宽度跟banner宽度换行</li>\n<li>&lt;b&gt;LinearLayout&lt;/b&gt;：线性布局，纵向布局</li>\n<li>&lt;b&gt;WaterfallLayout&lt;/b&gt;：瀑布流布局</li>\n<li>&lt;b&gt;OneAndNLayout&lt;/b&gt;：左边一个大图，右边N张小图的布局</li>\n<li>&lt;b&gt;StickyLayout&lt;/b&gt;：悬浮布局</li>\n</ul>\n<h3>整体架构</h3>\n<p>页面动态化 整体架构如下：</p>\n<br>\n<div class=\"image-package\">\n<div class=\"image-container\" style=\"max-width: 700px; max-height: 602px;\">\n<div class=\"image-container-fill\" style=\"padding-bottom: 86.07000000000001%;\"></div>\n<div class=\"image-view\" data-width=\"1522\" data-height=\"1310\"><img data-original-src=\"//upload-images.jianshu.io/upload_images/1932655-8a391b25583f328b.png\" data-original-width=\"1522\" data-original-height=\"1310\" data-original-format=\"image/png\" data-original-filesize=\"161541\"></div>\n</div>\n<div class=\"image-caption\">Screenshot 2017-04-27 19.10.17.png</div>\n</div>\n<p>每个UIViewController都是一个页面，页面是由一个个卡片组成的，卡片其实就是代表了一个布局样式，每个卡片是由一个个元素组成的，元素其实指的就是卡片布局样式下的各个banner，对应的JSON数据结构如下：</p>\n<pre><code>{\n    \"cards\": [\n        {\n            \"layout\": 1,\n            \"elements\": [\n                {\n                    \n                },\n                {\n                    \n                },\n                ...\n            ]\n        },\n        {\n            \"layout\": 1,\n            \"elements\": [\n                {\n                    \n                },\n                {\n                    \n                },\n                ...\n            ]\n        },\n        ...\n    ]\n}\n</code></pre>\n<h3>具体实现</h3>\n<p>由于大部分页面内容都需要上下滑动显示更多内容的，所以页面动态化的实现是基于<code>UICollectionView</code>来做的，当然也可以使用<code>UIScrollView</code>来实现（淘宝天猫都是通过一个自定义具有回收复用机制的<code>UIScrollView</code>来展示动态化页面内容的），我的理解是既然<code>UICollectionView</code>可以通过自定义<code>UICollectionViewLayout</code>来展现自定义的cell，为什么不用还需要自己定义一个具有回收复用机制的<code>UIScrollView</code>去展现内容，<code>UICollectionView</code>自身就是带有回收复用机制的。我的具体实现，先看下整体的UML图：</p>\n<div class=\"image-package\">\n<div class=\"image-container\" style=\"max-width: 700px; max-height: 261px;\">\n<div class=\"image-container-fill\" style=\"padding-bottom: 37.31%;\"></div>\n<div class=\"image-view\" data-width=\"3120\" data-height=\"1164\"><img data-original-src=\"//upload-images.jianshu.io/upload_images/1932655-4fda17b12938f621.png\" data-original-width=\"3120\" data-original-height=\"1164\" data-original-format=\"image/png\" data-original-filesize=\"386110\"></div>\n</div>\n<div class=\"image-caption\">Screenshot 2017-04-27 23.10.15.png</div>\n</div>\n<p>每个<code>KBBaseCard</code>代表了一个布局的卡片，对应<code>UICollectionView</code>里的一个<code>section</code>，<code>KBBaseCard</code>里elements的每个<code>KBBaseElement</code>代表了一个banner，对应<code>UICollectionView</code>里的一个<code>item</code>，<code>KBBaseElement</code>里的<code>KBElementStyle</code>提供banner需要展现的样式。<code>KBElementStyle</code>里的<code>type</code>字段是用来定义banner的样式，比方说<code>type=1</code>代表这个banner就是一张图。实际展示图如下：</p>\n<div class=\"image-package\">\n<div class=\"image-container\" style=\"max-width: 700px; max-height: 921px;\">\n<div class=\"image-container-fill\" style=\"padding-bottom: 131.65%;\"></div>\n<div class=\"image-view\" data-width=\"992\" data-height=\"1306\"><img data-original-src=\"//upload-images.jianshu.io/upload_images/1932655-f34fc93e9954cb47.png\" data-original-width=\"992\" data-original-height=\"1306\" data-original-format=\"image/png\" data-original-filesize=\"97042\"></div>\n</div>\n<div class=\"image-caption\">Screenshot 2017-04-27 23.21.04.png</div>\n</div>\n<h3>UICollectionViewLayout的自定义</h3>\n<p><code>UICollectionView</code>提供的<code>UICollectionViewFlowLayout</code>并不能满足我的对样式展现的需求，所以这里我们必须自己写一套<code>UICollectionViewLayout</code>来提供对<code>KBBaseElement</code>里<code>style</code>字段里的样式支持，核心方法如下，具体实现在本文中不做过多详解，大家可以自己查询一下相关的知识：</p>\n<pre><code>- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath\n{\n    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];\n    \n    NSInteger sectionLayout = 1;    // CardFlowLayout is the default layout\n    \n    if (self.delegate &amp;&amp; [self.delegate respondsToSelector:@selector(collectionView:layout:layoutForSection:)]) {\n        sectionLayout = [self.delegate collectionView:self.collectionView layout:self layoutForSection:indexPath.section];\n    }\n    \n    // CardFlowLayout 流式布局\n    if (sectionLayout == 1) {\n        [self handleLayoutAttributesForFlowLayout:layoutAttributes atIndexPath:indexPath];\n    }\n    // CardLinearLayout 线性布局\n    if (sectionLayout == 2) {\n        [self handleLayoutAttributesForLinearLayout:layoutAttributes atIndexPath:indexPath];\n    }\n    // CardOneAndNLayout 左边一张大图右边N张小图布局\n    if (sectionLayout == 3) {\n        [self handleLayoutAttributesForOneAndNLayout:layoutAttributes atIndexPath:indexPath];\n    }\n    // CardWaterfallLayout 瀑布流布局\n    if (sectionLayout == 4) {\n        [self handleLayoutAttributesForWaterfallLayout:layoutAttributes atIndexPath:indexPath];\n    }\n    \n    return layoutAttributes;\n}\n</code></pre>\n<h3>JSON数据</h3>\n<p>这里提供一个我自己创建的JSON mock数据，还有根据这个JSON数据最终展现出来的页面图</p>\n<ul>\n<li>JSON</li>\n</ul>\n<pre><code>{\n    \"cards\": [\n        {\n            \"layout\": 1,\n            \"style\": {\n                \"backgroundColor\": \"#ffffff\"\n            },\n            \"elements\": [\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/lqdbpar1xWZw5NuOB9ezf80UDm75Ei%2BZyMo%2BMgvE%2Bdunea_02T1704271119_480_240.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0\",\n                            \"0\",\n                            \"0.75\",\n                            \"0.75\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 2.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/i11BQFE2dWpzplHNBV1Tr8Hafa5zTz%2BCoxF%2BwIBS%2BKF2Vx_03T1704261946_480_240.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0\",\n                            \"0.75\",\n                            \"0.75\",\n                            \"0\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 2.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/KwaBThC4mguSnQCGBXVtfK4RmTTSeQ%2BZ5Ql%2BTNvQ%2BKNWzP_05T1704251128_480_240.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0.75\",\n                            \"0\",\n                            \"0\",\n                            \"0.75\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 2.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/G4LGkdg7si4p5ZzVBZUKmkTq7EeTKt%2BRRaE%2BfQyx%2B6JlfC_06T1704251128_480_240.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0.75\",\n                            \"0.75\",\n                            \"0\",\n                            \"0\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 2.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/HNkdnuFcFey5Om5GpNpO-C2LLgTrO3WtFT1704201935_960_210.jpg\",\n                    \"style\": {\n                        \"display\": \"block\",\n                        \"margin\": [\n                            \"0.75\",\n                            \"0\",\n                            \"0\",\n                            \"0\"\n                        ],\n                        \"width_height_ratio\": 4.57\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/mrui23TlZJcRAocRWB70T1704262151_600_375.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0.75\",\n                            \"0\",\n                            \"0\",\n                            \"0.75\"\n                        ],\n                        \"width_ratio\": 0.7,\n                        \"width_height_ratio\": 1.6\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/ppiV8ODszIZciIZbmQih-A6HUakFq6g6oLTNhT1704201914_474_555.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0.75\",\n                            \"0.75\",\n                            \"0\",\n                            \"0\"\n                        ],\n                        \"width_ratio\": 0.3,\n                        \"width_height_ratio\": 0.685\n                    }\n                }\n            ]\n        },\n        {\n            \"layout\": 3,\n                \"style\": {\n                \"backgroundColor\": \"#ffffff\"\n            },\n            \"elements\": [\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/KHASB80nO5JMsX1e2iChshdRT1704262150_480_480.jpg\",\n                    \"style\": {\n                        \"margin\": [\n                            \"0\",\n                            \"0\",\n                            \"0\",\n                            \"0.75\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 1.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/ygMuXb96hrtGtT6Wc9vZ2T17040101959_480_240.jpg\",\n                    \"style\": {\n                        \"margin\": [\n                            \"0\",\n                            \"0.75\",\n                            \"0.75\",\n                            \"0\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 2.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/ADB5IaeoJWP9pIzVmwzkfGKhT1704262151_480_240.jpg\",\n                    \"style\": {\n                        \"margin\": [\n                            \"0.75\",\n                            \"0.75\",\n                            \"0\",\n                            \"0\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 2.0\n                    }\n                }\n            ]\n        },\n        {\n            \"layout\": 1,\n            \"style\": {\n                \"backgroundColor\": \"#ffffff\"\n            },\n            \"elements\": [\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/ppiV8ODszIZciIZbmQih-A6HUakFq6g6oLTNhT1704201914_474_555.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0\",\n                            \"0\",\n                            \"0\",\n                            \"0.75\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 0.856\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/mbO7ln3VxpAWxKkw84Ay-ys6BLHmk9LLRvwa4T1704201914_474_555.jpg\",\n                    \"style\": {\n                        \"display\": \"inline\",\n                        \"margin\": [\n                            \"0\",\n                            \"0.75\",\n                            \"0\",\n                            \"0\"\n                        ],\n                        \"width_ratio\": 0.5,\n                        \"width_height_ratio\": 0.856\n                    }\n                }\n            ]\n        },\n        {\n            \"layout\": 2,\n            \"style\": {\n                \"backgroundColor\": \"#ffffff\"\n            },\n            \"elements\": [\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/hwCkTs9AoDThXhv5k38dr2M0T1704242204_960_480.jpg\",\n                    \"style\": {\n                        \"margin\": [\n                            \"0\",\n                            \"0\",\n                            \"10\",\n                            \"0\"\n                        ],\n                        \"width_height_ratio\": 2.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/Tt9TfxyDTSSirP27lXEiIsKPriT1704241519_960_480.jpg\",\n                    \"style\": {\n                        \"margin\": [\n                            \"0\",\n                            \"0\",\n                            \"10\",\n                            \"0\"\n                        ],\n                        \"width_height_ratio\": 2.0\n                    }\n                },\n                {\n                    \"type\": 1,\n                    \"image\": \"http://haitao.nos.netease.com/uey1sJ03lEOglRm626Th6dKrT1704242203_960_480.jpg\",\n                    \"style\": {\n                        \"margin\": [\n                            \"0\",\n                            \"0\",\n                            \"10\",\n                            \"0\"\n                        ],\n                        \"width_height_ratio\": 2.0\n                    }\n                }\n            ]\n        }\n    ]\n}\n</code></pre>\n<ul>\n<li>页面图</li>\n</ul>\n<div class=\"image-package\">\n<div class=\"image-container\" style=\"max-width: 700px; max-height: 1286px;\">\n<div class=\"image-container-fill\" style=\"padding-bottom: 183.82%;\"></div>\n<div class=\"image-view\" data-width=\"754\" data-height=\"1386\"><img data-original-src=\"//upload-images.jianshu.io/upload_images/1932655-766215c49f649735.png\" data-original-width=\"754\" data-original-height=\"1386\" data-original-format=\"image/png\" data-original-filesize=\"926318\"></div>\n</div>\n<div class=\"image-caption\">Screenshot 2017-04-27 23.29.39.png</div>\n</div>\n<div class=\"image-package\">\n<div class=\"image-container\" style=\"max-width: 700px; max-height: 1283px;\">\n<div class=\"image-container-fill\" style=\"padding-bottom: 183.29%;\"></div>\n<div class=\"image-view\" data-width=\"754\" data-height=\"1382\"><img data-original-src=\"//upload-images.jianshu.io/upload_images/1932655-fb2cb887b42dddbc.png\" data-original-width=\"754\" data-original-height=\"1382\" data-original-format=\"image/png\" data-original-filesize=\"1110378\"></div>\n</div>\n<div class=\"image-caption\">Screenshot 2017-04-27 23.29.59.png</div>\n</div>\n<blockquote>\n<p>转载请注明出处，原文地址：<a href=\"https://link.jianshu.com?t=http://kobedai.me/p9rsts-6s/\" target=\"_blank\" rel=\"nofollow\">http://kobedai.me/p9rsts-6s/</a></p>\n</blockquote>\n",
  "paid_type": "free",
  "notebook_id": 3975722,
  "commentable": true,
  "reprintable": true,
  "first_shared_at": "2017-04-27T23:32:53.000+08:00",
  "wordage": 1028,
  "paid_content_accessible": false,
  "share_image_url": "http://upload-images.jianshu.io/upload_images/1932655-8a391b25583f328b.png",
  "show_paid_comment_tips": false,
  "user": {
    "id": 1932655,
    "slug": "eedc12fb5b1f",
    "nickname": "Kobe_Dai",
    "gender": 1,
    "avatar": "https://cdn2.jianshu.io/assets/default_avatar/11-4d7c6ca89f439111aff57b23be1c73ba.jpg",
    "intro": "Mobile Developer @Shopify 🇨🇦",
    "intro_compiled": "Mobile Developer @Shopify 🇨🇦",
    "wordage": 10723,
    "likes_count": 470,
    "badges": [],
    "user_ip_addr": "安大略",
    "vip": null,
    "liked_by_user": false,
    "liked_user": false
  },
  "distribution_more_earn_percent": 24,
  "last_updated_at": 1512480867,
  "activity_collection_slug": null
}
